package org.jpropelleralt.reaction.impl;

import java.util.IdentityHashMap;
import java.util.Map;

import org.jpropelleralt.box.Box;
import org.jpropelleralt.change.Change;
import org.jpropelleralt.list.ListBox;
import org.jpropelleralt.list.impl.ListChangeDefault;
import org.jpropelleralt.list.impl.ListDeltaDefault;
import org.jpropelleralt.plumbing.Source;
import org.jpropelleralt.reaction.Reaction;

/**
 * Simple reaction operating on a set of
 * input {@link Box}es and producing a result
 * that is set into a fixed output {@link ListBox}
 * 
 * @param <T>		The type of value
 */
public class ReactionList<T> implements Reaction {

	private final Source<Iterable<T>> resultSource;
	private final ListBox<T> target;
	
	/**
	 * Create a {@link ReactionList}
	 * @param resultSource		The source of results - whenever {@link Source#get()} is called,
	 * 							it must return the value to set into the targetRef. This must only 
	 * 							read values from the inputs.
	 * @param target			The {@link ListBox} whose contents will be replaced by the result returned by
	 * 							resultSource, when any of the inputs changes.
	 * @param inputs			The inputs - when these change, the target will be updated. Must include
	 * 							ALL data required by the calculation in resultSource.
	 */
	public ReactionList(Source<Iterable<T>> resultSource, ListBox<T> target, Box... inputs) {
		this.resultSource = resultSource;
		this.target = target;
		
		//We must exist as long as target does
		target.features().registerReaction(this);
		
		//We react to changes in all inputs
		for (Box box : inputs) {
			box.features().addReaction(this);
		}
		
		//FIXME this shouldn't be necessary - when a new constraint is made,
		//it should be able to declare it WILL make a change, rather than having to make
		//it immediately
		//Start out up to date
		apply(true);
	}
	@Override
	public boolean completeChanges(Map<Box, Change> changes) {
		//Nothing to do
		return false;
	}

	@Override
	public void apply(boolean propagate) {
		target.reactionReplace(resultSource.get(), propagate);
		//Set the target ref to the result of calculation
	}

	@Override
	public Map<Box, Change> react(Map<Box, Change> changes) {
		IdentityHashMap<Box, Change> reactChanges = new IdentityHashMap<Box, Change>();
		reactChanges.put(target, ListChangeDefault.shallow(ListDeltaDefault.newCompleteChange()));
		return reactChanges;
	}

}
